package main

import (
	"bytes"
	"errors"
	"fmt"
	"gitee.com/kinwyb/appTools/manager"
	"io"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
)

func removeOldFile(filename string) error {
	_, err := os.Stat(filename)
	if os.IsNotExist(err) {
		return nil
	}
	err = os.Remove(filename)
	if err != nil {
		manager.ServiceLog.WithError(err).Error("移除旧文件失败")
		return err
	}
	return nil
}

// 下载文件
func downloadFile(url string, localPath string, fb func(length, downLen int64)) error {
	url = strings.TrimSpace(url)
	var (
		fsize   int64
		buf     = make([]byte, 32*1024)
		written int64
	)
	tmpFilePath := localPath + ".download"
	manager.ServiceLog.Infof("开始下载[%s]:%s", url, tmpFilePath)
	//创建一个http client
	client := new(http.Client)
	//client.Timeout = time.Second * 60 //设置超时时间
	//get方法获取资源
	resp, err := client.Get(url)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		manager.ServiceLog.Errorf("下载文件[%s]请求失败:%d", tmpFilePath, resp.StatusCode)
		return errors.New("下载请求失败:" + resp.Status)
	}
	//读取服务器返回的文件大小
	fSizeString := resp.Header.Get("Content-Length")
	if fSizeString != "" {
		fsize, err = strconv.ParseInt(fSizeString, 10, 64)
		if err == nil {
			err = removeOldFile(localPath)
			if err != nil {
				return err
			}
			manager.ServiceLog.Infof("下载文件[%s]大小: %d", tmpFilePath, fsize)
		}
	}
	//创建文件
	file, err := os.Create(tmpFilePath)
	if err != nil {
		return err
	}
	//defer file.Close()
	if resp.Body == nil {
		return errors.New("body is null")
	}
	//defer resp.Body.Close()
	if fb != nil {
		//下面是 io.copyBuffer() 的简化版本
		fsize = 0
		for {
			//读取bytes
			nr, er := resp.Body.Read(buf)
			if nr > 0 {
				//写入bytes
				nw, ew := file.Write(buf[0:nr])
				//数据长度大于0
				if nw > 0 {
					written += int64(nw)
				}
				//写入出错
				if ew != nil {
					err = ew
					break
				}
				//读取是数据长度不等于写入的数据长度
				if nr != nw {
					err = io.ErrShortWrite
					break
				}
			}
			if er != nil {
				if er != io.EOF {
					err = er
				}
				break
			}
			fsize = fsize + int64(nr)
			//没有错误了快使用 callback
			fb(fsize, written)
		}
	} else {
		_, err = io.Copy(file, resp.Body)
	}
	resp.Body.Close()
	file.Close() //关闭文件
	if err == nil {
		err = os.Rename(tmpFilePath, localPath)
		if err != nil {
			manager.ServiceLog.WithField("info", tmpFilePath).WithError(err).Error("文件重命名失败")
		}
		manager.ServiceLog.Infof("文件下载已完成:%s", localPath)
	} else {
		manager.ServiceLog.WithField("info", url).WithError(err).Error("文件下载失败")
	}
	return err
}

// 下载执行文件
func downExecFile(filename string, version string) error {
	if !strings.HasPrefix(httpAddress, "http://") {
		httpAddress = "http://" + httpAddress
	}
	url := httpAddress + "/v1/file/down?os=" + runtime.GOOS +
		"&file=" + filename +
		"&version=" + version
	if runtime.GOOS == "windows" {
		filename = filename + ".exe"
	}
	localPath := defaultAppDir + "/" + filename
	return downloadFile(url, localPath, nil)
}

// http请求
func httpRequest(req manager.CmdHttpRequest) ([]byte, error) {
	if req.Method == "" {
		req.Method = http.MethodGet
	}
	var body io.Reader
	if req.Body != nil && len(req.Body) > 0 {
		body = bytes.NewReader(req.Body)
	} else {
		body = nil
	}
	request, err := http.NewRequest(req.Method, req.Url, body)
	if err != nil {
		return nil, err
	}
	if len(req.Headers) > 0 {
		for k, v := range req.Headers {
			request.Header.Add(k, v)
		}
	}
	manager.ServiceLog.Error(req.Url)
	http.DefaultClient.Timeout = 2 * time.Minute
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		return nil, err
	}
	if response.Body != nil {
		defer response.Body.Close()
	}
	if response.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("请求失败:%d", response.StatusCode)
	}
	return io.ReadAll(response.Body)
}
